<template>
  <div class="glb-viewer fill-width-height">
    <!-- 模型渲染容器 -->
    <div ref="container" class="viewer-container"></div>

    <!-- 加载状态指示器 -->
    <div v-if="isLoading" class="loading-overlay">
      <div class="spinner"></div>
      <p>加载中: {{ loadProgress.toFixed(0) }}%</p>
    </div>

    <!-- 错误信息显示 -->
    <div v-if="errorMessage" class="error-message">
      <p>{{ errorMessage }}</p>
      <button @click="clearError" class="error-close">×</button>
    </div>

    <!-- 优化布局的文件上传控件 -->
    <div v-if="allowFileUpload" class="file-upload-container">
      <label class="file-upload-label" :class="{ 'is-dragover': isDragover }">
        <input
          type="file"
          accept=".glb,.gltf"
          @change="handleFileUpload"
          @dragover.prevent="isDragover = true"
          @dragleave.prevent="isDragover = false"
          @drop.prevent="handleDrop"
          class="file-upload-input"
        >
        <div class="upload-icon">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M12 5V19" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
            <path d="M5 12L12 5L19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
          </svg>
        </div>
        <div class="upload-text">
          <p class="upload-main-text">{{ selectedFileName || '上传模型文件' }}</p>
          <p class="upload-sub-text" v-if="!selectedFileName">支持GLB/GLTF格式</p>
        </div>
      </label>

      <button
        v-if="selectedFileName"
        @click="clearSelectedFile"
        class="clear-upload"
      >
        清除
      </button>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three/build/three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

export default {
  name: 'GlbViewer',
  props: {
    modelUrl: {
      type: String,
      default: ''
    },
    allowFileUpload: {
      type: Boolean,
      default: true
    },
    width: {
      type: String,
      default: '100%'
    },
    height: {
      type: String,
      default: '100%'
    },
    // 优化为更明亮的渐变背景
    backgroundColor: {
      type: String,
      default: 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%)'
    }
  },
  data() {
    return {
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      model: null,
      isLoading: false,
      loadProgress: 0,
      errorMessage: '',
      isDragover: false,
      selectedFileName: ''
    };
  },
  watch: {
    modelUrl(newVal) {
      if (newVal) {
        this.clearScene();
        this.loadModelFromUrl(newVal);
      }
    },
    width() {
      this.updateRendererSize();
    },
    height() {
      this.updateRendererSize();
    },
    backgroundColor(newVal) {
      if (this.scene) {
        if (newVal.includes('gradient')) {
          this.$refs.container.style.background = newVal;
        } else {
          this.scene.background = new THREE.Color(newVal);
        }
      }
    }
  },
  mounted() {
    this.initScene();
    if (this.modelUrl) {
      this.loadModelFromUrl(this.modelUrl);
    }
    window.addEventListener('resize', this.handleWindowResize);
    this.$refs.container.style.width = this.width;
    this.$refs.container.style.height = this.height;

    if (this.backgroundColor.includes('gradient')) {
      this.$refs.container.style.background = this.backgroundColor;
    }
  },
  beforeUnmount() {
    window.removeEventListener('resize', this.handleWindowResize);
    this.clearScene();
    if (this.renderer) {
      this.renderer.dispose();
    }
  },
  methods: {
    initScene() {
      this.scene = new THREE.Scene();

      if (!this.backgroundColor.includes('gradient')) {
        this.scene.background = new THREE.Color(this.backgroundColor);
      }

      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      this.scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
      directionalLight.position.set(5, 10, 7.5);
      this.scene.add(directionalLight);

      const container = this.$refs.container;
      const width = container.clientWidth;
      const height = container.clientHeight;

      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      this.camera.position.z = 5;

      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: this.backgroundColor.includes('gradient')
      });
      this.renderer.setSize(width, height);
      container.appendChild(this.renderer.domElement);

      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.dampingFactor = 0.1;
      this.controls.enableZoom = true;
      this.controls.enablePan = true;

      this.animate();
    },

    animate() {
      requestAnimationFrame(() => this.animate());
      if (this.controls) {
        this.controls.update();
      }
      if (this.renderer && this.scene && this.camera) {
        this.renderer.render(this.scene, this.camera);
      }
    },

    loadModelFromUrl(url) {
      this.isLoading = true;
      this.errorMessage = '';

      const loader = new GLTFLoader();
      loader.load(
        url,
        (gltf) => {
          this.model = gltf.scene;
          this.scene.add(this.model);
          this.autoAdjustCamera();
          this.isLoading = false;
        },
        (xhr) => {
          this.loadProgress = (xhr.loaded / xhr.total) * 100;
        },
        (error) => {
          console.error('模型加载错误:', error);
          this.errorMessage = `模型加载失败: ${error.message}`;
          this.isLoading = false;
        }
      );
    },

    handleFileUpload(event) {
      const file = event.target.files[0];
      this.handleFileSelection(file);
    },

    handleDrop(event) {
      this.isDragover = false;
      const file = event.dataTransfer.files[0];
      this.handleFileSelection(file);
    },

    handleFileSelection(file) {
      if (!file) return;

      if (!file.name.endsWith('.glb') && !file.name.endsWith('.gltf')) {
        this.errorMessage = '请上传GLB或GLTF格式的模型文件';
        return;
      }

      this.selectedFileName = file.name;
      this.clearScene();
      this.isLoading = true;
      this.errorMessage = '';

      const fileUrl = URL.createObjectURL(file);
      const loader = new GLTFLoader();

      loader.load(
        fileUrl,
        (gltf) => {
          this.model = gltf.scene;
          this.scene.add(this.model);
          this.autoAdjustCamera();
          this.isLoading = false;
          URL.revokeObjectURL(fileUrl);
          this.handleWindowResize()
        },
        (xhr) => {
          this.loadProgress = (xhr.loaded / xhr.total) * 100;
        },
        (error) => {
          console.error('本地文件加载错误:', error);
          this.errorMessage = `文件加载失败: ${error.message}`;
          this.isLoading = false;
          URL.revokeObjectURL(fileUrl);
        }
      );
    },

    clearSelectedFile() {
      this.selectedFileName = '';
      const input = this.$el.querySelector('.file-upload-input');
      if (input) input.value = '';
    },

    clearError() {
      this.errorMessage = '';
    },

    autoAdjustCamera() {
      if (!this.model) return;

      const box = new THREE.Box3().setFromObject(this.model);
      const center = box.getCenter(new THREE.Vector3());
      const size = box.getSize(new THREE.Vector3());

      const maxDim = Math.max(size.x, size.y, size.z);
      const fov = this.camera.fov * (Math.PI / 180);
      let cameraZ = Math.abs(maxDim / 2 / Math.tan(fov / 2));
      cameraZ *= 1.5;

      this.camera.position.z = cameraZ;
      this.controls.target.set(center.x, center.y, center.z);
      this.controls.update();
    },

    handleWindowResize() {
      this.updateRendererSize();
    },

    updateRendererSize() {
      if (!this.camera || !this.renderer || !this.$refs.container) return;

      const container = this.$refs.container;
      const width = container.clientWidth;
      const height = container.clientHeight;

      this.camera.aspect = width / height;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(width, height);
    },

    clearScene() {
      if (this.model && this.scene) {
        this.scene.remove(this.model);
        this.model.traverse((object) => {
          if (object.geometry) object.geometry.dispose();
          if (object.material) {
            if (Array.isArray(object.material)) {
              object.material.forEach(material => material.dispose());
            } else {
              object.material.dispose();
            }
          }
        });
        this.model = null;
      }
    }
  }
};
</script>

<style scoped>
.glb-viewer {
  position: relative;
  display: inline-block;
}

.viewer-container {
  position: relative;
  overflow: hidden;
  transition: all 0.3s ease;
}

/* 加载状态 */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(4px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  z-index: 10;
  transition: opacity 0.3s ease;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(66, 135, 245, 0.15);
  border-left-color: #4287f5;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 16px;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* 错误信息 */
.error-message {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  max-width: 80%;
  padding: 12px 20px;
  background-color: #ff5252;
  color: white;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 10px;
  animation: slideIn 0.3s ease;
}

.error-close {
  background: none;
  border: none;
  color: white;
  font-size: 18px;
  cursor: pointer;
  padding: 0 4px;
  line-height: 1;
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translate(-50%, -20px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

/* 优化布局的文件上传控件 - 放置在右上角，不遮挡模型内容 */
.file-upload-container {
  position: absolute;
  top: 20px;
  right: 10px; /* 靠右放置，避免遮挡中心内容 */
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  width: auto;

}

.file-upload-label {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  background-color: rgba(255, 255, 255, 0.95);
  border: 2px dashed #ccd6e0;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.3s ease;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  white-space: nowrap;
}

.file-upload-label.is-dragover {
  border-color: #4287f5;
  background-color: rgba(66, 135, 245, 0.05);
}

.file-upload-input {
  display: none;
}

.upload-icon {
  color: #4287f5;
  flex-shrink: 0;
}

.upload-text {
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
}

.upload-main-text {
  margin: 0 0 2px 0;
  font-weight: 500;
  color: #2d3748;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.upload-sub-text {
  margin: 0;
  font-size: 11px;
  color: #718096;
}

.clear-upload {
  padding: 5px 10px;
  background-color: #f7fafc;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
  font-size: 12px;
  color: #4a5568;
  cursor: pointer;
  transition: all 0.2s ease;
}

.clear-upload:hover {
  background-color: #edf2f7;
  color: #2d3748;
}
</style>
